#pragma once 

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include "util.hpp"
#include "log.hpp"
#include <string>
#include <vector>
#include <sstream>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/wait.h>
#include  <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unordered_map>
#include <algorithm>

#define SEP ": "
#define WEB_ROOT "webroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
  std::string desc;
  switch(code)
  {
    case 200:
      desc="ok";
      break;
    case 404:
      desc="not found";
      break;
    default:
      break;
  }
  return desc;
}
static std::string Suffux2Desc(const std::string suffix)
{
  std::unordered_map<std::string,std::string>suffix2desc={
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"},
  };
  auto iter=suffix2desc.find(suffix);
  if(iter!=suffix2desc.end())
  {
    return iter->second;
  }
  return LINE_END;
}
//请求
class HttpRequest
{
  public:
    std::string request_line;
    std::vector<std::string> rquest_header;
    std::string blank;
    std::string request_boday;

    //分析结果
    std::string method;
    std::string uri;
    std::string version;

    std::unordered_map<std::string,std::string> header_kv;
    int content_length;
    std::string path;
    std::string suffix;
    std::string query_string;

    bool cgi;
    int size;
  public:
    HttpRequest():content_length(0),cgi(false),size(0){}
    ~HttpRequest(){}
};
//响应
class HttpResponse
{
  public:
    std::string response_line;
    std::vector<std::string> response_header;
    std::string blank;
    std::string resqonse_boday;

    int status_code;
    int fd;
  public:
    HttpResponse():blank(LINE_END),status_code(OK),fd(-1){}
    ~HttpResponse(){}
};

//读取请求，请求分析， 构建响应 响应
class EndPoint
{
  private:
    int sock;
    HttpRequest http_request;
    HttpResponse http_response;

    bool stop;
  private:
    bool RecvHttpRequestLine()
    {
     auto& line=http_request.request_line;
     if(util::Readline(sock,line)>0)
     {
      line.resize(line.size()-1);
      // LOG(INFO,http_request.request_line);
     }else 
     {
       stop=true;
     }
     return stop;
    }

    bool RecvHttpRequestHeader()
    {
      std::string line;
      while(true)
      {
        line.clear();
        if(util::Readline(sock,line)<=0)
        {
          stop=true;
          break;
        }
        if(line=="\n")
        {
          http_request.blank=line;
          break;
        }
        line.resize(line.size()-1);
        http_request.rquest_header.push_back(line);
       LOG(INFO,line);
      }
      return stop;
    }
    void ParseHttpRequestline()
    {
      auto& line=http_request.request_line;
      std::stringstream ss(line);
      ss>>http_request.method>>http_request.uri>>http_request.version;
      auto& method=http_request.method;
      std::transform(method.begin(),method.end(),method.begin(),::toupper);
      //std::cout<<method<<std::endl;
       LOG(INFO,http_request.method);
      LOG(INFO,http_request.uri);
      LOG(INFO,http_request.version);
    }
    void ParseHttpRequestheader()
    {
      std::string key;
      std::string value;
      for(auto &iter:http_request.rquest_header)
      {
        if(util::CutString(iter,key,value,SEP))
        {
          http_request.header_kv.insert({key,value}); 
          std::cout<<key<<std::endl;
          std::cout<<value<<std::endl;
        }
      }
    }
    bool IsNeedRecvHttpRequestBody()
    {
      auto& method=http_request.method;
      if(method=="POST")
      {
        auto& header_kv=http_request.header_kv;
        auto iter=header_kv.find("Content-Length");
        if(iter!=header_kv.end())
        {
          http_request.content_length=atoi(iter->second.c_str());
          return true;
        }
      } 
      return false;
    }
    bool RecvHttpRequsetBody()
    {
      if(IsNeedRecvHttpRequestBody())
      {
        int content_length=http_request.content_length;
        auto& boday=http_request.request_boday;

        char ch=0;
        while(content_length)
        {
          ssize_t s=recv(sock,&ch,1,0);
          if(s>0)
          {
            boday.push_back(ch);
            content_length--;
          }else
          {
            stop=true;
            break;
          }
        }
        LOG(INFO,boday);
      }
      return stop;
    }

    int ProcessCgi()
    {
      int code=OK;
      auto& response_body=http_response.resqonse_boday;
      auto& bin=http_request.path;
      auto& query_string=http_request.query_string;//GET
      auto& body_text=http_request.request_boday;//POST
      auto& method=http_request.method;
      int  content_length=http_request.content_length;

      std::string method_env;
      std::string query_string_env;
      std::string content_length_env;

      int input[2];//父进程读
      int output[2];//父进程写

      if(pipe(input)<0)
      {
        LOG(ERROR,"pipe inout error");
        return  SERVER_ERROR;
      }
      if(pipe(output)<0)
      {
        LOG(ERROR,"pipe output error");
        return SERVER_ERROR;
      }

      pid_t pid=fork();
      if(pid==0)
      {
        //子进程
        close(input[0]);
        close(output[1]);

        //子进程的数据读取于写出
        // input[1] 替换 标准输出
        // output[0] 替换 标准输入
        dup2(output[0],0);
        dup2(input[1],1);

        //把请求方法 通过环境变量添加到子进程的共享区中，子进程在程序替换时继承下来
        method_env="METHOD=";
        method_env+=method;
        putenv((char*)method_env.c_str());

        if("GET"==method)
        {
          query_string_env="QUERY_STRING=";
          query_string_env+=query_string;
          putenv((char*)query_string_env.c_str());
        }else if("POST"==method)
        {
          content_length_env="CONTENT_LENGTH=";
          content_length_env+=std::to_string(content_length);
          putenv((char*)content_length_env.c_str());
         // LOG(INFO,"Post Method ,Add Content_Length env");
        }else 
        {

        }

        execl(bin.c_str(),bin.c_str(),nullptr);
        exit(1);
      }else if(pid<0)
      {
        LOG(ERROR,"fork error");
        return SERVER_ERROR;
      }else 
      {
        //父进程
        close(input[1]);
        close(output[0]);

        if(method=="POST")
        {
           const char* start=body_text.c_str();
          int size=0;
          int total=0;
          while(total<content_length&&(size=write(output[1],start+total,body_text.size()+total))>0)
          {
            total+=size;
          }
        }

        char ch=0;
        while(read(input[0],&ch,1)>0)
        {
          response_body.push_back(ch);
        }
        int status;
        pid_t ret=waitpid(pid,&status,0);
        if(ret==pid)
        {
          if(WIFEXITED(status))
          {
            if(WEXITSTATUS(status)==0)
            {
              code=OK;
            }
          }else
          {
            code=BAD_REQUEST;
          }
        }else
        {
          code=SERVER_ERROR;
        }
        close(input[0]);
        close(output[1]);
      }
      return code;
    }

    int ProcessNonCgi()
    {
      http_response.fd=open(http_request.path.c_str(),O_RDONLY);
      if(http_response.fd>=0)
      {
      return OK;
      }
      return 404;
    }
    void HeadlerError(std::string page)
    {
      //std::cout<<page<<std::endl;
      http_request.cgi=false;//在cgi错误时直接 false 在发送响应时直接以错误页面的形式响应
      http_response.fd=open(page.c_str(),O_RDONLY);
      if(http_response.fd>=0)
      {
        struct stat st;
        stat(page.c_str(),&st);
        http_request.size=st.st_size;
        std::string line="Content-Type: text/html";
        line+=LINE_END;
        http_response.response_header.push_back(line);
        line="Content-Length: ";
        line+=std::to_string(st.st_size);
        line+=LINE_END;
        http_response.response_header.push_back(line);
      }
    }
    void BuildOkResponse()
    {
        std::string line="Content-Type: ";
        line+=Suffux2Desc(http_request.suffix);
        line+=LINE_END;
        http_response.response_header.push_back(line);
        
        line="Content-Length: ";
        if(http_request.cgi)
        {
          line+=std::to_string(http_response.resqonse_boday.size());
        }else
        {
        line+=std::to_string(http_request.size);
        }
        line+=LINE_END;
        http_response.response_header.push_back(line);

    }
    void  BuildHttpResponseHelper()
    {
      auto& code=http_response.status_code;
      auto& status_line=http_response.response_line;
      status_line=HTTP_VERSION;
      status_line+=" ";
      status_line+=std::to_string(code);
      status_line+=" ";
      status_line+=Code2Desc(code);
      status_line+=LINE_END;

      std::string path=WEB_ROOT;
      path+="/";
      switch(code)
      {
        case OK:
          BuildOkResponse();
          break;
        case NOT_FOUND:
          path+=PAGE_404;
          HeadlerError(path);
          break;
        case BAD_REQUEST:
          path+=PAGE_404;
          HeadlerError(path);
          break;
        case SERVER_ERROR:
          path+=PAGE_404;
          HeadlerError(path);
          break;
        default:
          break;
      }
    }
  public:
    EndPoint(int _sock):sock(_sock),stop(false)
    {}
    bool IsStop()
    {
      return stop;
    }
    void RecvHttpRequest()
    {
     if( (!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))
     {
       ParseHttpRequestline();
       ParseHttpRequestheader();
       RecvHttpRequsetBody();
     }
    }
   /* void ParseHttpRequest(){}*/
    void BuildHttpResponse()
    {
      std::string _path;
      struct stat st;
      std::size_t found=0;
      auto& code=http_response.status_code;
      if(http_request.method!="GET"&&http_request.method!="POST")
      {
        LOG(WARNING,"method error");
        code=BAD_REQUEST;
        goto END;
      }
      if(http_request.method=="GET")
      {
        size_t pos=http_request.uri.find('?');
        if(pos!=std::string::npos)
        {
          util::CutString(http_request.uri,http_request.path,http_request.query_string,"?");
          http_request.cgi=true;
        }else 
        {
          http_request.path=http_request.uri;
        }
      }else if(http_request.method=="POST")
      {
        http_request.cgi=true;
        http_request.path=http_request.uri;

      }else
      {

      }
      _path=http_request.path;
      http_request.path=WEB_ROOT;
      http_request.path+=_path;
      if(http_request.path[http_request.path.size()-1]=='/')
      {
        http_request.path+=HOME_PAGE;
      }

      if(stat(http_request.path.c_str(),&st)==0)
      {
        //资源存在
        if(S_ISDIR(st.st_mode))
        {
          //说明是一个目录，并且不是/结束的
          http_request.path+="/";
          http_request.path+=HOME_PAGE;
          stat(http_request.path.c_str(),&st);
        }
        if( (st.st_mode&S_IXUSR) || (st.st_mode& S_IXGRP)|| (st.st_mode&S_IXOTH) )
        {
          //说明资源是可执行程序
          http_request.cgi=true;
        }
        http_request.size=st.st_size;
      }else 
      {
        std::string info=http_request.path;
        info+="不合法";
        LOG(WARNING,info);
        code=NOT_FOUND;
        goto END;
      }

      found=http_request.path.rfind(".");
      if(found==std::string::npos)
      {
        http_request.suffix=".html";
      }else 
      {
        http_request.suffix=http_request.path.substr(found);
      }

      if(http_request.cgi)
      {
        //有数据要处理，使用CGI方法
       code= ProcessCgi();
      }else
      {
        //没有数据
       code= ProcessNonCgi();
      }
END:
      BuildHttpResponseHelper();
    } 
    void printf()
    {
      std::cout<<http_request.method<<std::endl;
      std::cout<<http_request.uri<<std::endl;
      std::cout<<http_request.path<<std::endl;
      std::cout<<http_request.query_string<<std::endl;
    }
    void SendHttpResponse()
    {
      send(sock,http_response.response_line.c_str(),http_response.response_line.size(),0);
      for(auto& iter:http_response.response_header)
      {
        send(sock,iter.c_str(),iter.size(),0);
      }
      send(sock,http_response.blank.c_str(),http_response.blank.size(),0);
      if(http_request.cgi)
      {
        auto& resqonse_boday=http_response.resqonse_boday;
        size_t size=0;
        size_t total=0;
        const char* start=resqonse_boday.c_str();
        while(total<resqonse_boday.size() && (size=send(sock,start+total,resqonse_boday.size()-total,0))>0)
        {
          total+=size;
        }
      }else
      {
      sendfile(sock,http_response.fd,nullptr,http_request.size);
      close(http_response.fd);
      }
    }
    ~EndPoint()
    {
      close(sock);
    }

};
class CallBack
{
  public:
    CallBack()
    {}
    void operator()(int sock)
    {
      HandlerRequest(sock);
    }
    void HandlerRequest(int sock)
    {
      LOG(INFO, "Hander Request Begin");
#ifdef DUBUG 
      char buf[1024];
     int s= recv(sock,buf,1023,0);
      buf[s]=0;
      std::cout<<buf<<std::endl;
#else 
     // std::cout<<sock<<" "<<std::endl;
      
      EndPoint* ep=new EndPoint(sock);
      ep->RecvHttpRequest();
      if(!ep->IsStop())
      {
        LOG(INFO,"Recv No Error,Begin Build And Send");
        ep->BuildHttpResponse();
        ep->SendHttpResponse();
      }else
      {
        LOG(WARNING,"Recv Error,Stop Build And Send");
      }
      delete ep;
#endif     
      LOG(INFO,"Hander Request End ");
      close(sock);
    }
};
